/**
 Übung 2 - Fliegender Wuerfel
================================
Thematik: Dynamik mit newtonischen Gesetzen
Grundgesetzt der Dynamik : F = m * a (Beziehung zwischen Kraft, Masse und Beschleunigung

Merke: Wenn Kraft und Masse gegeben sind kann daraus die Beschleunigung und daraus wiedermum
mittels Integration die Geschwidnigkeit v und die Position x berechnet werden

Übung 2 - Schiefer Wurf eines Tennisballs bzw. Würfels

Gegeben: V0: Abschussgeschwindigkeit und alpha: Abschusswinkel

x(t),y(t): Position zur Zeit t: Betrachte die Wirkung der Kräfte auf Masse

Mit Newtonischen Gesetzten --> Komponentenweise Behandlung

Fx = m * ax (x-Richtung)
Fy = m * ay (y-Richtung)

Kraft auf m :
F x = 0		in X-Richtung ist die Beschleunigung = 0 d.h. es ist eine gleichförmige Bewegung ax = 0
			mit Anfangsgeschwindigkeit v0 * cos (alpha)
Fy = - mg 	in Y-Richtung ist die Beschleunigung konstant: ay = -g, vy = v0 * sin(alpha)

Mittels Integration der Geschwindigkeit kann Positio ermittelt werden:
x(t) = Integral ( v0 * cos (alpha)) 			= v0 * cos(alpha) * t
y(t) = Integral ( v0 * sin(alpha) - g * t )		= v0 * sin(a) * t - g/2 * t^2



 **/

package wuerfel;

//  -------------   JOGL SampleProgram  (Ellipse) ------------

import gui.DemoGui2;
import gui.DoubleProperty;
import gui.IUebung;

import javax.media.opengl.*;

import com.sun.opengl.util.FPSAnimator;
import com.sun.opengl.util.GLUT;

import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;

public class U2_Wuerfel implements WindowListener, GLEventListener, KeyListener, IUebung {
	private GLCanvas canvas; // OpenGL-Canvas
	private double xmin = -20;
	private double xmax = 20;
	private double ymin = -3;
	private double ymax = 3;

	//double t, dt = 0.01;

	//startwerte fuer Abschusswinkel und Startgeschwindigkeit
	private double v0 = 10;
	private double angle = 45;
	
	private DoubleProperty speedprop = new DoubleProperty("Geschwdinigkeit", 0, 30, 0.5, 10);
	private DoubleProperty angleprop = new DoubleProperty("Abschusswinkel", 0, 180, 1, 45);
	
	private FPSAnimator anim;

	private static final double gravity = 9.81;
	
	private ArrayList<Tennisball> tbs;
	
	private ArrayList<Cube> cubes;
	
	private GLUT glut;

	private static final int font = GLUT.BITMAP_HELVETICA_12;
	
	private static final double crosshairsize = 0.25;
	
	private static boolean showDrehAchse = false;
	
	
	// ------------------ Methoden --------------------

	@Override
	public void start() {
		if (!anim.isAnimating()){
			anim.start();			
		}
	}

	@Override
	public void stop() {
		if (anim.isAnimating()){
			anim.stop();
		}	
	}

	public void modeSwtich(int mode) {
		// TODO Auto-generated method stub
		
	}
	
	public void ToggleDrehAchse(){
		showDrehAchse = !showDrehAchse;
	}
	
	public DoubleProperty[] getProperties(){
		DoubleProperty[] res = {speedprop,angleprop};
		return res;
	}
	
	public void shootWuerfel(){
		cubes.add(new Cube(this.speedprop.getCurrent(),this.angleprop.getCurrent()));
	}
	
	public void shootTennisBall(){
		tbs.add(new Tennisball(0, 0, 1, this.speedprop.getCurrent(), this.angleprop.getCurrent()));
	}

	// Koordinatenachsen zeichnen
	void zeichneAchsen(GL gl) {
		gl.glBegin(GL.GL_LINES);
		// x-Achse
		gl.glVertex2d(xmin, 0);
		gl.glVertex2d(xmax, 0);
		// y-Achse
		gl.glVertex2d(0, ymin);
		gl.glVertex2d(0, ymax);
		gl.glEnd();
	}

	// Zeichne ein simples Menue
	void zeichneMenu(GL gl) {
		gl.glRasterPos2d(xmin, ymax - 1);
		glut.glutBitmapString(font, "V0 " + this.speedprop.getCurrent());
		gl.glRasterPos2d(xmin, ymax - 1.5);
		glut.glutBitmapString(font, "Angle " + this.angleprop.getCurrent());
	}
	
	// Zeichne Crosshair zum Zielen
	
	void zeichneCrossHair(GL gl){
		double cx = this.speedprop.getCurrent()/5 * Math.cos(Math.toRadians(angleprop.getCurrent()));
		double cy = this.speedprop.getCurrent()/5 * Math.sin(Math.toRadians(angleprop.getCurrent()));
		gl.glPushMatrix();
		gl.glColor3d(1, 1, 0);
		gl.glTranslated(cx, cy, 0);
		gl.glBegin(GL.GL_LINES);
		gl.glVertex2d(-crosshairsize, 0);
		gl.glVertex2d(crosshairsize, 0);
		gl.glVertex2d(0, -crosshairsize);
		gl.glVertex2d(0, crosshairsize);
		gl.glEnd();
		gl.glPopMatrix();
	}
	
	private class Cube{
		
		private double xpos;
		private double ypos;
		
		//Drehachse
		private double dx;
		private double dy;
		private double dz;
		
		private double v0 = 10;
		
		private double alpha = 45;
		private double beta = 10, dbeta=1;
		
		private double t, dt = 0.01;
		
		//detemines size of cubes
		private double edge = 1.0;
		
		public Cube(double speed, double angle){
			this.v0 = speed;
			this.alpha = angle;
			this.dx = Math.random();
			this.dy = Math.random();
			this.dz = Math.random();
			
		}
		
		public void draw (GL gl){
			/**
				Berechnung der neuen Positionen (Skript S.17: Wurfparabel)
				Zugrundeliegendes Gesetzt: Newtonische Gleichung: F = m * a
				kann als Vektorgleichung angesehen werden --> zerlegen in x und y Komponente
					Fx = m * ax , 
					Fy = m * ay
					
				Betrachtet man eine Masse M in der Luft wirken darauf folgende Kräfte:
				
				In x-Richtung Fx = 0 : Kraft 0 ==> gleichförmige Bewegung mit Anfangs-geschwindigkeit:
					x(t) = v0 * cos (alpha) * t
					
				In y-Richtung Beschleungigung konstant -g: Fy = m *ay = m * -g -> ay = -g
				
				Geschwindigkeit zum Zeitpunkt t: vy(t) = v0 * sin(alpha) - g * t
				Mit Integration kommt man auf die Formel für y Position zum Zeitpunkt t 
					y(t) = v0 * sin(alpha) * t - g/2 * t^2
				
			 **/
			xpos = v0 * Math.cos(Math.toRadians(alpha)) * t; 
			ypos = v0 * Math.sin(Math.toRadians(alpha)) * t - (0.5*gravity) * t * t;
			
			if (ypos < ymin){
				cubes.remove(this);
			}
			
			
			gl.glPushMatrix();
			
			gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL);
			gl.glLoadIdentity();
			//Verschieben des W�rfelns an neue Position
			gl.glTranslated(xpos,ypos,0);
			//W�rfel um beta an der Drehachse drehen
			gl.glRotated(beta, dx, dy, dz);
			
			//zeichnen aller 4 seiten eines wuerfels
			//eckpunkte im uhrzeigersinn angeben
			gl.glColor3f(1, 0, 0);
			gl.glBegin(GL.GL_QUADS);
			//Vorne
			gl.glColor3f(1, 0, 0);
			gl.glVertex3d(-edge, -edge, -edge);
			gl.glVertex3d(edge, -edge, -edge);
			gl.glVertex3d(edge, edge, -edge);
			gl.glVertex3d(-edge, edge, -edge);
			//Links
			gl.glColor3f(0, 1, 0);
			gl.glVertex3d(-edge, -edge, -edge);
			gl.glVertex3d(-edge, -edge, edge);
			gl.glVertex3d(-edge, edge, edge);
			gl.glVertex3d(-edge, edge, -edge);
			//Rechts
			gl.glColor3f(0, 0, 1);
			gl.glVertex3d(edge, -edge, edge);
			gl.glVertex3d(edge, -edge, -edge);
			gl.glVertex3d(edge, edge, -edge);
			gl.glVertex3d(edge, edge, edge);
			//Hinten
			gl.glColor3f(1, 1, 0);
			gl.glVertex3d(-edge, -edge, -edge);
			gl.glVertex3d(edge, -edge, -edge);
			gl.glVertex3d(edge, -edge, edge);
			gl.glVertex3d(-edge, -edge, edge);
			//Oben
			gl.glColor3f(0, 1, 1);
			gl.glVertex3d(-edge, -edge, edge);
			gl.glVertex3d(edge, -edge, edge);
			gl.glVertex3d(edge, edge, edge);
			gl.glVertex3d(-edge, edge, edge);
			//Unten
			gl.glColor3f(1, 1, 1);
			gl.glVertex3d(-edge, edge, edge);
			gl.glVertex3d(-edge, edge, -edge);
			gl.glVertex3d(edge, edge, -edge);
			gl.glVertex3d(edge, edge, edge);
			
			gl.glEnd();

			//Zeichnet Drehachse falls erw�nscht
			if (showDrehAchse){
				double size = 2;
				gl.glBegin(GL.GL_LINES);
				gl.glColor3d(1, 0, 0);
				gl.glVertex3d(0 - size * edge*dx, 0 - size * edge*dy, 0 - size * edge*dz);
				gl.glVertex3d(size * edge * dx, size * edge * dy, size * edge*dz);
				gl.glEnd();
			}

			gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE);
			
			gl.glPopMatrix();
			
			
			//Aktualisierungs des Zeitschritts
			t+=dt;
			//Drehwinkel vergr�sseren
			beta+=dbeta;
			
		}
	}
	
	/**
	 * OpenGL doesn't have a function to draw a circle. So we have to draw multiple polygons in order to draw a circle
	 * We can visualize circle as cobination of very small triangles spread like a Japanese Fan - draw 50 triangles
	 * arranged in a fan fashion
	 * @author Chris
	 *
	 */
	private class Tennisball{
		
		//centre of circle at the beginning
		private double xstart;
		private double ystart;
		//X und Y Position waehrend des Wurfs
		private double xpos;
		private double ypos;
		//radius
		private double radius;
		//v0 startgeschwindigkeit
		private double v0;
		//abschusswinkel
		private double alpha;
		
		private double t, dt = 0.01;
		
		public Tennisball(double xstart, double ystart, double radius, double speed, double angle){
			this.xstart = xstart;
			this.ystart = ystart;
			this.radius = radius;
			this.v0 = speed;
			this.alpha = angle;
		}
		
		public void draw(GL gl){
			//Berechnung der neuen Positionen siehe skript s.17
			xpos = this.v0 * Math.cos(Math.toRadians(alpha)) * t; 
			ypos = this.v0 * Math.sin(Math.toRadians(alpha)) * t - (0.5*gravity) * t * t;
			
			//if ball is not visible -> remove from list
			if (ypos < ymin){
				tbs.remove(this);
			}
			
			gl.glPushMatrix();
			
			gl.glTranslated(xstart + xpos, ystart + ypos, 0);
			
			
			// Making circle in 50 small triangles
			double increment = 2 * Math.PI / 50;
			// Defining radius of circle equal to 70 pixels
			// We want to draw circle in red colour
			gl.glColor4f(1, 0, 0, 1);
			// Starting loop for drawing triangles
			for (double angle = 0; angle < 2 * Math.PI; angle += increment) {
				gl.glBegin(GL.GL_POLYGON);
				// One vertex of each triangle is at center of circle
				gl.glVertex2d(xstart, ystart);
				// Other two vertices form the periphery of the circle
				gl.glVertex2d(xstart + Math.cos(angle) * radius,
						ystart + Math.sin(angle) * radius);
				gl.glVertex2d(xstart + Math.cos(angle + increment) * radius, ystart
						+ Math.sin(angle + increment) * radius);
				gl.glEnd();
			}
			
			gl.glPopMatrix();
			
			t+=dt;
		}
	}

	// Konstruktor
	public U2_Wuerfel() {
		tbs = new ArrayList<Tennisball>();
		cubes = new ArrayList<Cube>();
		Frame f = new Frame("Fliegender Wuerfel");
		f.setSize(800, 600);
		f.addWindowListener(this);
		// OpenGL-Window
		canvas = new GLCanvas();
		canvas.addKeyListener(this);
		canvas.addGLEventListener(this);
//		canvas.addMouseMotionListener(this);
		f.add(canvas);
		f.setVisible(true);
		canvas.requestFocus();
		glut = new GLUT();
	}
	
	   public U2_Wuerfel(GLJPanel jp){
			tbs = new ArrayList<Tennisball>();
			cubes = new ArrayList<Cube>();
	        jp.addKeyListener(this);
	        jp.addGLEventListener(this);
	        jp.setVisible(true);
	        jp.requestFocus();
	        glut = new GLUT();
	    }

	public static void main(String[] args) // main-Methode der Applikation
	{
		new U2_Wuerfel();
	}

	// --------- OpenGL-Events -----------------------

	public void init(GLAutoDrawable drawable) {
		GL gl = drawable.getGL(); // OpenGL-Objekt
		gl.glClearColor(0.68f, 0.84f, 0.9f, 1.0f); // erasing color
		anim = new FPSAnimator(drawable, 200, true);
		anim.start();
		//tb = new Tennisball(0, 0, 1, 10, 45);
		//cube = new Cube();
		
	}

	@SuppressWarnings("unchecked")
	public void display(GLAutoDrawable drawable) {
		GL gl = drawable.getGL();
		gl.glClear(GL.GL_COLOR_BUFFER_BIT); // Bild loeschen
		gl.glMatrixMode(GL.GL_MODELVIEW);
		gl.glColor3d(0, 0, 0);
		zeichneAchsen(gl);
//		zeichneMenu(gl);
		zeichneCrossHair(gl);
		gl.glColor3d(1, 1, 0.1);
		for(Tennisball b: (ArrayList<Tennisball>)tbs.clone()){
			b.draw(gl);
		}
		for(Cube c: (ArrayList<Cube>)cubes.clone()){
			c.draw(gl);
		}
		gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE);
		gl.glColor3d(0, 1, 1); // Zeichenfarbe
		
		DemoGui2.setStatusText("V0: " + speedprop.getCurrent() +"\t\t" +  "Angle: " + angleprop.getCurrent());
		
		// t wir zeichnen den Kreis jeweils immer zu einer gewissen Zeit
		// t += 2*dt;
	}

	public void reshape(GLAutoDrawable drawable, // Window resized
			int x, int y, int width, int height) {
		GL gl = drawable.getGL();
		// damit keine Verzerrungen enstehen
		ymax = xmax * height / width;
		ymin = -ymax;
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		gl.glOrtho(xmin, xmax, ymin, ymax, -6, 6);
		gl.glViewport(0, 0, width, height);
	}

	public void displayChanged(GLAutoDrawable drawable, boolean modeChanged,
			boolean deviceChanged) {
	}

	// --------- Window-Events --------------------

	public void windowClosing(WindowEvent e) {
		System.exit(0);
	}

	public void windowActivated(WindowEvent e) {
	}

	public void windowClosed(WindowEvent e) {
	}

	public void windowDeactivated(WindowEvent e) {
	}

	public void windowDeiconified(WindowEvent e) {
	}

	public void windowIconified(WindowEvent e) {
	}

	public void windowOpened(WindowEvent e) {
	}


	@Override
	public void keyPressed(KeyEvent e) {
		switch (e.getKeyCode()) {
		case KeyEvent.VK_S:
			if (anim.isAnimating()) {
				anim.stop();
			} else {
				anim.start();
			}
			break;
		case KeyEvent.VK_ENTER:
			cubes.add(new Cube(this.speedprop.getCurrent(),this.angleprop.getCurrent()));
			break;
		case KeyEvent.VK_BACK_SPACE:
			tbs.add(new Tennisball(0, 0, 1, this.speedprop.getCurrent(), this.angleprop.getCurrent()));
			break;
		case KeyEvent.VK_UP:
			this.v0 += 0.5;
			break;
		case KeyEvent.VK_DOWN:
			this.v0 -= 0.5;
			break;
		case KeyEvent.VK_LEFT:
			this.angle+=1;
			break;
		case KeyEvent.VK_RIGHT:
			this.angle-=1;
			break;
		case KeyEvent.VK_D:
			showDrehAchse = !showDrehAchse;
		default:
			break;
		}
	}

	@Override
	public void keyReleased(KeyEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void keyTyped(KeyEvent e) {

	}

}